﻿using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using Unity.Collections;

internal class CustomPassSSPR : CustomPass
{
    // It can be used to configure render targets and their clear state. Also to create temporary render target textures.
    // When empty this render pass will render to the active camera render target.
    // You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
    // The render pipeline will ensure target setup and clearing happens in an performance manner.

    [System.Serializable]
    public class PassSetting
    {
        public string CmdName = "屏幕空间平面反射";

        /// <summary>
        /// 是否启用SSPR
        /// </summary>
        [Header("Settings")]
        public bool ShouldRenderSSPR = true;

        /// <summary>
        /// 用于捕捉屏幕反射内容的ComputerShader
        /// </summary>
        public ComputeShader SsprComputerShader;

        /// <summary>
        /// 反射采样贴图大小
        /// </summary>
        [Range(128, 1024)]
        public int RT_height = 512;

        /// <summary>
        /// 反射结果的色调调整
        /// </summary>
        [ColorUsage(true, true)]
        public Color TintColor = Color.white;

        /// <summary>
        /// SSPR插入的渲染阶段
        /// </summary>
        public CustomPassInjectionPoint EventPlugIn = CustomPassInjectionPoint.BeforePostProcess;
    }

    private PassSetting Setting;

    private static int ReflectColorRT = Shader.PropertyToID("_ReflectColor");//定义一张RT用来存储反射图像的颜色 当颜色缓冲区使
    private static int HashPackedDataRT = Shader.PropertyToID("_HashPackedData"); //定义一张RT来保存对应关系
    private static int CameraDepthTexture = Shader.PropertyToID("_CameraDepthTexture");
    private static int CameraOpaqueTexture = Shader.PropertyToID("_CameraOpaqueTexture");

    private RenderTargetIdentifier ReflectColorRT_ID = new RenderTargetIdentifier(ReflectColorRT);
    private RenderTargetIdentifier HashPackedDataRT_ID = new RenderTargetIdentifier(HashPackedDataRT);
    private RenderTargetIdentifier CameraDepthTexture_ID = new RenderTargetIdentifier(CameraDepthTexture);
    private RenderTargetIdentifier CameraOpaqueTexture_ID = new RenderTargetIdentifier(CameraOpaqueTexture);

    private ShaderTagId lightMode_SSPR_sti;

    private const int SHADER_NUMTHREAD_X = 8; //must match compute shader's [numthread(x)]
    private const int SHADER_NUMTHREAD_Y = 8; //must match compute shader's [numthread(y)]

    public ComputeShader SsprComputerShader;

    private int RTWidth => Mathf.RoundToInt(RTHeight * (float)Screen.width / Screen.height);//真实的rt水平大小

    [Range(256, 1080)]
    public int RTHeight = 512;//真实的rt垂直大小

    public float ReflectPlaneHeight = 0f;

    public Color FinalTintColor = Color.white;

    [Range(0.01f, 1f)]
    public float FadeOutScreenBorderWidthVerticle = 0.25f;

    [Range(0.01f, 1f)]
    public float FadeOutScreenBorderWidthHorizontal = 0.35f;

    public FilteringSettings FilteringSettings = new FilteringSettings(RenderQueueRange.transparent);

    private int ThreadCountX => Mathf.RoundToInt((float)RTWidth / SHADER_NUMTHREAD_X); //水平方向上的组数
    private int ThreadCountY => Mathf.RoundToInt((float)RTHeight / SHADER_NUMTHREAD_Y); //竖直方向上的组数
    private int ThreadCountZ => 1; //Z方向组数

    [HideInInspector] public string passName = "ScreenSpacePlaneReflectionPass";

    protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
    {
        RTHeight = 512;
        // Setup code here
    }

    protected override void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera hdCamera, CullingResults cullingResult)
    {
        if (SsprComputerShader == null)
        {
            Debug.LogError("SSPRcomputeShader丢失！");
            return;
        }

        if (enabled)
        {
            int ThreadCountX = this.ThreadCountX;
            int ThreadCountY = this.ThreadCountY;
            int ThreadCountZ = this.ThreadCountZ;

            RenderTextureDescriptor rtd = new RenderTextureDescriptor(RTWidth, RTHeight, RenderTextureFormat.Default, 0, 0);//ARGB是用来存颜色通道的 这个图要传给shader 注意我们要a当遮罩 精度我认为单通道8字节足够了
            rtd.sRGB = false;
            rtd.enableRandomWrite = true;//绑定uav

            //ColorRT
            rtd.colorFormat = RenderTextureFormat.ARGBFloat;
            cmd.GetTemporaryRT(ReflectColorRT, rtd);

            //PackedDataRT - HashRT
            rtd.colorFormat = RenderTextureFormat.RInt;
            cmd.GetTemporaryRT(HashPackedDataRT, rtd);

            cmd.SetComputeVectorParam(SsprComputerShader, Shader.PropertyToID("_RTSize"), new Vector2(RTWidth, RTHeight));
            cmd.SetComputeFloatParam(SsprComputerShader, Shader.PropertyToID("_ReflectPlaneHeight"), ReflectPlaneHeight);
            cmd.SetComputeVectorParam(SsprComputerShader, Shader.PropertyToID("_CameraDirection"), hdCamera.camera.transform.forward);
            cmd.SetComputeVectorParam(SsprComputerShader, Shader.PropertyToID("_FinalTintColor"), FinalTintColor);
            cmd.SetComputeFloatParam(SsprComputerShader, Shader.PropertyToID("_FadeOutScreenBorderWidthVerticle"), FadeOutScreenBorderWidthVerticle);
            cmd.SetComputeFloatParam(SsprComputerShader, Shader.PropertyToID("_FadeOutScreenBorderWidthHorizontal"), FadeOutScreenBorderWidthHorizontal);

            Camera camera = hdCamera.camera;
            Matrix4x4 VP = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true) * camera.worldToCameraMatrix;
            cmd.SetComputeMatrixParam(SsprComputerShader, Shader.PropertyToID("_VPMatrix"), VP);
            cmd.SetComputeMatrixParam(SsprComputerShader, Shader.PropertyToID("_IVPMatrix"), VP.inverse);
            //cmd.SetComputeMatrixParam(SsprComputerShader, "_VPMatrix", hdCamera.mainViewConstants.viewProjMatrix);
            //cmd.SetComputeMatrixParam(SsprComputerShader, "_IVPMatrix", hdCamera.mainViewConstants.invViewProjMatrix); //HDRP中获得的是转置结果，所以要转置回去

            RTHandle colorBuffer, depthBuffer;
            GetCameraBuffers(out colorBuffer, out depthBuffer);

            rtd.colorFormat = RenderTextureFormat.Default;
            var tempID = new RenderTargetIdentifier(Shader.PropertyToID("_TempTexture"));
            cmd.GetTemporaryRT(Shader.PropertyToID("_TempTexture"), rtd);
            //清除历史影响
            int kernel_PathClear = SsprComputerShader.FindKernel("PathClear");
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathClear, "HashRT", HashPackedDataRT_ID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathClear, "ColorRT", ReflectColorRT_ID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathClear, "_CameraOpaqueTexture", colorBuffer.nameID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathClear, "_CameraDepthTexture", depthBuffer.nameID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathClear, "_TempTexture", tempID);
            cmd.DispatchCompute(SsprComputerShader, kernel_PathClear, ThreadCountX, ThreadCountY, ThreadCountZ);

            //绘制HashRT关系映射
            int kernel_PathRenderHashRT = SsprComputerShader.FindKernel("PathRenderHashRT");
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathRenderHashRT, "HashRT", HashPackedDataRT_ID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathRenderHashRT, "_CameraOpaqueTexture", colorBuffer.nameID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathRenderHashRT, "_CameraDepthTexture", depthBuffer.nameID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathRenderHashRT, "_TempTexture", tempID);
            cmd.DispatchCompute(SsprComputerShader, kernel_PathRenderHashRT, ThreadCountX, ThreadCountY, ThreadCountZ);

            //将反射结果绘制到ColorRT
            int kernel_PathResolveColorRT = SsprComputerShader.FindKernel("PathResolveColorRT");
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathResolveColorRT, "_CameraOpaqueTexture", colorBuffer.nameID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathResolveColorRT, "_CameraDepthTexture", depthBuffer.nameID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathResolveColorRT, "ColorRT", ReflectColorRT_ID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_PathResolveColorRT, "HashRT", HashPackedDataRT_ID);
            cmd.DispatchCompute(SsprComputerShader, kernel_PathResolveColorRT, ThreadCountX, ThreadCountY, ThreadCountZ);

            //补洞
            int kernel_FillHoles = SsprComputerShader.FindKernel("FillHoles");
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_FillHoles, "ColorRT", ReflectColorRT_ID);
            cmd.SetComputeTextureParam(SsprComputerShader, kernel_FillHoles, "HashRT", HashPackedDataRT_ID);
            cmd.DispatchCompute(SsprComputerShader, kernel_FillHoles, Mathf.CeilToInt(ThreadCountX / 2f), Mathf.CeilToInt(ThreadCountY / 2f), ThreadCountZ);

            //cmd.ReleaseTemporaryRT(HashPackedDataRT);
        }
        renderContext.ExecuteCommandBuffer(cmd);
        cmd.Clear();
        renderContext.Submit();

        DrawingSettings drawingSettings = new DrawingSettings(lightMode_SSPR_sti, new SortingSettings(hdCamera.camera));//CreateDrawingSettings(lightMode_SSPR_sti, ref renderingData, SortingCriteria.CommonOpaque);
        FilteringSettings filteringSettings = FilteringSettings.defaultValue;
        renderContext.DrawRenderers(cullingResult, ref drawingSettings, ref filteringSettings);

        renderContext.Submit();
    }

    protected override void Cleanup()
    {
    }
}